<template>
  <div class="gis-map-component">
    <div
      id="map"
      ref="mapBox"
      class="map-class"
      @drop.stop.prevent="dragAddLayerToMap"
      @dragover="$event.preventDefault()"
    >
      <!-- 缩放控件自定义 -->
      <gis-zoom-control :get-zoom="getZoom" @scale="mapZoom" />
    </div>
    <div v-show="mapLoading" class="layer-loading">
      <a-spin size="large" :delay="500" tip="Loading..."></a-spin>
    </div>
    <!-- 搜索 -->
    <gis-search
      :visible="currentToolBarAction === 'gisSearchVisible'"
      @fit-bounds="mapBoxFitBounds"
    />
    <!-- 测绘 -->
    <gis-mapping :visible="currentToolBarAction === 'gisMappingVisible'" />
    <!-- 数理分析 -->
    <mathematical-analysis
      :visible="currentToolBarAction === 'mathematicalAnalysisVisible'"
    />
    <!-- 图例 -->
    <gis-map-legend
      :base-id="'build'"
      :visible="mapLegend"
      :map-box-instance="mapBox"
      :parent-container="'gis-map-component'"
      :draggable="true"
      :overall-folding="false"
    />
    <!-- <a-menu style="position: absolute;" :style="{top:`${y}px`, left:`${x}px`}" v-if="contextMenuVisible" slot="overlay" @click="contextMenuVisible = false">
      <a-menu-item key="0"
        >到数据集</a-menu-item
      >
      <a-menu-item key="1"
        >到本地</a-menu-item
      >
    </a-menu> -->
  </div>
</template>

<script lang="ts">
/**
 * gis 地图画布
 */
import { Component, Prop, Vue, Watch, Mixins } from 'vue-property-decorator'
import mapboxgl from 'mapbox-gl'
import 'mapbox-gl/dist/mapbox-gl.css'
import MapboxDraw from '@mapbox/mapbox-gl-draw'
// @ts-ignore
import DrawRectangle from 'mapbox-gl-draw-rectangle-mode'
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'
import GisZoomControl from '@/components/gis/gis-analysis/GisZoomControl.vue'
import elementResizeDetectorMaker from 'element-resize-detector'
import { getMapTilesByMapTypeAndStyle } from '@/components/gis/map-package/map-config'
import GisStore, { LayerData, Tab } from '@/store/modules/gis'
import GisSearch from '@/components/gis/gis-analysis/analysis-drag-modal/GisSearch.vue'
import GisMapping from '@/components/gis/gis-analysis/analysis-drag-modal/GisMapping.vue'
import MathematicalAnalysis from '@/components/gis/gis-analysis/analysis-drag-modal/MathematicalAnalysis.vue'
import * as turf from '@turf/turf'
import GisContextMenu from './GisContextMenu.vue'
import { debounce } from 'lodash'
import mapUtils from '@/components/gis/map-package/map-utils'
import GisMapMixin from '@/components/gis/gis-analysis/gis-map-mixin'
import GisMapLegend from '@/components/gis/gis-analysis/analysis-drag-modal/GisMapLegend.vue'

@Component({
  components: {
    GisZoomControl,
    GisSearch,
    GisMapping,
    MathematicalAnalysis,
    GisContextMenu,
    GisMapLegend,
  },
})
export default class GisMap extends Mixins(GisMapMixin) {
  public testPublic: boolean = false
  @Prop({ type: Object }) toolBarSetting!: any // 显示比例尺 显示经纬度 显示图例
  @Prop({ type: Number }) currentTabId!: number // id
  public projectId: number | null = null

  public loadingState: boolean = false
  public contextMenuVisible: boolean = false

  public lng: number = 0 // 经纬度
  public lat: number = 0
  public x: number = 0 // 经纬度
  public y: number = 0
  public ponits: any = [] // 存储画的点

  // mapBox draw
  public mapBoxDraw: any // = new MapboxDraw()

  public get mode() {
    return GisStore.mode
  }

  // map 全局 loading
  public get mapLoading(): boolean {
    return GisStore.mapLoading
  }

  public created() {
    this.projectId = Number(this.$route.params.id)
  }

  // 分析工具分析生成的新图层
  public get analysisCreateLayer(): any {
    return GisStore.analysisCreateLayer
  }

  /**
   * 分析工具生成新的图层 （几何中心， 缓冲区 等）
   * @param layer
   */
  @Watch('analysisCreateLayer')
  public async onAnalysisCreateLayer(layer: any) {
    if (layer) {
      await this.checkLayerDataBeforeAddLayer(layer)
      GisStore.setAnalysisCreateLayer(null)
      await this.mapBoxFitBoundsByLayer(layer)
    }
  }
  @Watch('mode')
  public modeChange() {
    if (this.mode === 'distance') {
      this.clearLineData()
      this.mapBoxDraw.changeMode('draw_line_string')
      // console.log('画线')
      this.mapBox.on('click', this.clickFunc)
      this.mapBox.on('dblclick', () => {
        this.movePopup.remove()
        this.mapBox.off('mousemove', this.moveFunc)
        this.mapBox.off('click', this.clickFunc)
      })
    }
  }

  public popupArray: any = []
  public clickFunc($event: any) {
    // @ts-ignore
    const clickPoint = turf.point([$event.lngLat.lat, $event.lngLat.lng])
    clickPoint.properties = { title: '起点' }

    const popup = new mapboxgl.Popup({
      closeButton: true,
      closeOnClick: false, // false才能显示多个pop
    })
    if (this.ponits.length > 0) {
      const from = this.ponits[0]
      // @ts-ignore
      const to = turf.point([$event.lngLat.lat, $event.lngLat.lng])
      const options: any = { units: 'miles' }

      const distance = turf.distance(from, to, options)
      if (distance !== 0) {
        this.ponits.unshift(clickPoint)
        popup
          .setLngLat($event.lngLat)
          .setHTML(`<div>${distance}</div>`)
          .setMaxWidth('300px')
          .addTo(this.mapBox)
      }
    } else {
      this.ponits.unshift(clickPoint)
      popup
        .setLngLat($event.lngLat)
        .setHTML('<div>起点</div>')
        .setMaxWidth('300px')
        .addTo(this.mapBox)
    }
    this.mapBox.on('mousemove', this.moveFunc)
    this.popupArray.unshift(popup)
  }

  public movePopup: any = new mapboxgl.Popup({
    closeButton: false,
    closeOnClick: false,
  })
  public moveFunc($event: any) {
    const from = this.ponits[0]
    // @ts-ignore
    const to = turf.point([$event.lngLat.lat, $event.lngLat.lng])
    const options: any = { units: 'miles' }

    const distance = turf.distance(from, to, options)
    to.properties = { title: distance.toFixed(2) }

    this.movePopup
      .setLngLat($event.lngLat)
      .setHTML(`<div>${distance}</div>`)
      .setMaxWidth('300px')
      .addTo(this.mapBox)
  }

  public clearLineData() {
    this.ponits = []
    // this.popupArray.forEach((element: Popup) => {
    //   element.remove()
    // })
    // this.popupArray = []
  }

  /**
   * 修改底图
   */
  public updateSetting(settingInfo: any) {
    if (this.mapBox) {
      const { setting } = settingInfo
      switch (setting) {
        case 'gis-tile-style': {
          // 底图切换
          const { mapType, layerType } = settingInfo
          this.mapBox.setStyle(getMapTilesByMapTypeAndStyle(mapType, layerType))
          this.$nextTick(() => {
            this.initAddLayerOfVisible()
          })
          break
        }
        case 'scale': // 比例尺
          this.addOrRemoveScaleControl(settingInfo.scale)
          break
        case 'legend': // 图例
          this.mapLegend = settingInfo.legend
          break
        case 'lnglat': // 地理位置
          this.addCoordinatesControlMixin(settingInfo.lnglat)
          break
        default:
          break
      }
    }
  }

  public get oldTabInfo() {
    return GisStore.oldTabInfo
  }

  @Watch('currentTabId', { immediate: true })
  public watchCurrentTabId(value: number) {
    if (value) {
      this.updateMap(value)
    }
  }

  public async updateMap(value: number) {
    await this.queryLayersByTabId(value) // 获取图层信息
    // 当前图层的配置信息
    const currentTabSetting: any = GisStore.gisTabs.filter(
      (tab: Tab) => tab.tabId === value
    )[0]?.data || {
      layerType: 1,
      legend: false,
      lnglat: true,
      mapType: 1,
      scale: true,
    }
    // 首先清除上张地图
    if (this.mapBox) {
      const setting: any = this.oldTabInfo?.data
      // 配置 是否需要更换底图
      if (setting && currentTabSetting.layerType !== setting.layerType) {
        this.mapBox.setStyle(
          getMapTilesByMapTypeAndStyle(1, currentTabSetting.layerType)
        )
        this.$nextTick(() => {
          this.initAddLayerOfVisible()
        })
      } else {
        this.initAddLayerOfVisible()
      }
      // 经纬度
      if (setting && currentTabSetting.lnglat !== setting.lnglat) {
        this.addCoordinatesControlMixin(currentTabSetting.lnglat)
      }
      // 比例尺
      if (setting && currentTabSetting.scale !== setting.scale) {
        this.addOrRemoveScaleControl(currentTabSetting.scale)
      }
      // 图例
      if (setting && setting.legend !== currentTabSetting.legend) {
        this.mapLegend = currentTabSetting.legend
      }
    } else {
      this.$nextTick(() => {
        if (value) {
          this.initMap(currentTabSetting)
          this.mapResize()
        }
      })
    }
  }

  /**
   * 获取该 tab 下的所有图层
   */
  public async queryLayersByTabId(tabId: number | null) {
    if (tabId) {
      const parameters: any = {
        projectId: this.projectId,
        tabId,
      }
      await GisStore.queryLayersById(parameters)
    }
  }

  /**
   * 清空图层， 重新画新的图层
   * 暂无使用
   */
  public clearLayer() {
    mapUtils.clearMapLayersByLayers(GisStore.mapboxInstance, GisStore.layerList)
  }

  // @Watch('contextMenuVisible')
  // public menuVisibleChange(value: boolean){
  //   if (!value){
  //     this.mapBox.dragRotate.enable()
  //     this.mapBox.dragPan.enable()
  //   }else{
  //     this.mapBox.dragPan.disable()
  //   }
  // }
  // 表格选择的行数据
  public get tableSelectRowDatas() {
    return GisStore.tableSelectRowDatas
  }

  // 表格选择的行更新
  @Watch('tableSelectRowDatas')
  public onTableSelectRowDatas(rows: any) {
    // TODO 高亮
    console.log(rows, 'ddddd')
  }

  /**
   * 画布尺寸调整
   */
  public mapResize() {
    const rootErd = elementResizeDetectorMaker()
    const mapBox = this.$refs.mapBox as HTMLElement
    rootErd.listenTo(mapBox, this.mapBoxResizeDebounce)
    this.$once('hook:beforeDestroy', () => {
      rootErd.removeAllListeners(mapBox)
    })
  }
  // 防抖
  public mapBoxResizeDebounce = debounce(() => this._mapBoxResize(), 500)
  private _mapBoxResize() {
    this.mapBox.resize()
  }

  /**
   * 实例化地图
   */
  public async initMap(currentTabSetting: any) {
    GisStore.setMapLoading(true)
    this.mapBox = new mapboxgl.Map({
      container: 'map', // container ID
      style: getMapTilesByMapTypeAndStyle(1, currentTabSetting.layerType), // 天地图
      bounds: [
        [73.536163, 53.558094],
        [135.084259, 7.397329],
      ],
      center: [106.1157366, 37.95462],
      zoom: 4, // starting zoom
      maxZoom: 18,
      minZoom: 1.5,
      dragPan: true,
      preserveDrawingBuffer: true,
      // antialias: true,  // 抗锯齿
      // logoPosition: 'top-right',
      attributionControl: true,
      renderWorldCopies: true, // 地图缩小时将渲染多个全局地图的副本
    })

    this.mapBox.on('load', () => {
      if (currentTabSetting.lnglat) {
        this.addCoordinatesControlMixin(true)
      }
      if (currentTabSetting.scale) {
        this.addOrRemoveScaleControl(true)
      }
      if (currentTabSetting.legend) {
        this.mapLegend = true // 初始化图例
      }
      // this.addMapBoxDraw() // 添加画笔插件
      this.initAddLayerOfVisible() // 初始化 加载可视状态的图层
      this.addMouseEvent()
      this.initAndUpdateMapboxOnMouseEvent()
    })

    this.initContextMenu() // 右键菜单
    this.mapBox.on('click', () => {
      this.removeContextMenu()
    })
    GisStore.setMapboxInstance(this.mapBox) //  store 存储 mapbox 实例
  }

  public menuPopup: any = new mapboxgl.Popup({
    closeButton: false,
    closeOnClick: false,
  })
  // 右键菜单
  public initContextMenu() {
    this.mapBox.on('contextmenu', ($event: any) => {
      // this.contextMenuVisible = true
      // this.x = $event.point.x
      // this.y = $event.point.y
      const points = this.selectBoxPoints.features[0].geometry.coordinates[0]
      const lngArray: any = []
      const latArray: any = []
      const { lng, lat } = $event.lngLat
      points.forEach((element: any) => {
        lngArray.push(element[0])
        latArray.push(element[1])
      })

      if (
        lng < Math.max(...lngArray) &&
        lng > Math.min(...lngArray) &&
        lat < Math.max(...latArray) &&
        lat > Math.min(...latArray)
      ) {
        const P = Vue.extend(GisContextMenu)
        const vm = new P()
        vm.$mount()

        this.menuPopup
          .setLngLat($event.lngLat)
          .setDOMContent(vm.$el)
          .setMaxWidth('300px')
          .addTo(this.mapBox)
      }
    })
  }

  public removeContextMenu() {
    this.menuPopup.remove()
  }

  /**
   * 初始化 / 更新地图全局鼠标事件
   */
  public initAndUpdateMapboxOnMouseEvent() {
    this.mapBox.off('mousemove', ($event: any) => this.globalMouseEvent($event))
    this.mapBox.on('mousemove', ($event: any) => this.globalMouseEvent($event))
  }

  /**
   * 地图图层添加鼠标事件 (高亮， tooltip)
   */
  public addMouseEventToMapBoxByLayerId(layerId: string) {
    this.mapBox.on('mouseleave', layerId, ($event: any) => {
      this.mapBox.getCanvas().style.cursor = ''
      this.mapboxPopup.remove()
      mapUtils.hoverSetFeatureState(this.mapBox, $event, layerId, 'mouseleave')
    })
  }

  // 已存在的图层 id
  private get exitLayerIds() {
    return this.mapBox
      .getStyle()
      .layers.filter((layer: any) => {
        return !layer.id.includes('label')
      })
      .map((layer: any) => layer.id)
  }

  /**
   * 获取指定图层的配置信息
   * @param layerId
   */
  public getLayerAttributeTagOfDetailByLayerId(layerId: string): string[] {
    const currentLayer: any = GisStore.layerList.find(
      (layer: any) => layer.id === Number(layerId)
    )
    if (
      currentLayer.data?.tagInfo?.attributeTag &&
      currentLayer.data.tagInfo.attributeTag.length > 0
    ) {
      const detailField: string[] = currentLayer.data.tagInfo.attributeTag
        .filter((tag: any) => tag.tagType === 'detail')
        .map((tag: any) => tag.name)
      return [...new Set(detailField)]
    }
    return []
  }

  /**
   * 全局鼠标事件
   * TODO 需要修改
   */
  public globalMouseEvent($event: any) {
    $event.preventDefault()
    const features = this.mapBox.queryRenderedFeatures($event.point, {
      layers: this.exitLayerIds,
    })
    if (features.length > 0) {
      const [firstLayerFeature, ...otherLayerFeatures] = features
      // 其他不高亮
      if (otherLayerFeatures.length > 0) {
        otherLayerFeatures
          .filter((item: any) => item.layer.type !== 'symbol')
          .forEach((feature: any) => {
            this.mapBox.setFeatureState(
              { source: `source-${feature.layer.id}`, id: feature.id },
              { hover: false }
            )
          })
      }
      // TODO hover 高亮
      if (firstLayerFeature.layer.type !== 'symbol') {
        mapUtils.globalHoverSetFeatureState(
          this.mapBox,
          firstLayerFeature,
          'mousemove'
        )
        this.mapBox.getCanvas().style.cursor = 'pointer'
        const coordinates = firstLayerFeature.geometry.coordinates.slice()
        const currentFeatureProperty: any = firstLayerFeature.properties
        // delete currentFeatureProperty._record_id_
        const popupShowFields: string[] =
          this.getLayerAttributeTagOfDetailByLayerId(firstLayerFeature.layer.id)
        if (popupShowFields.length > 0) {
          let attributesDom = ''
          popupShowFields.forEach((key: any) => {
            attributesDom += `<div class="attr-key-value">${key}: ${currentFeatureProperty[key]}</div>`
          })
          while (Math.abs($event.lngLat.lng - coordinates[0]) > 180) {
            coordinates[0] += $event.lngLat.lng > coordinates[0] ? 360 : -360
          }
          this.mapboxPopup
            .setLngLat($event.lngLat)
            .setHTML(`<div>${attributesDom}</div>`)
            .setMaxWidth('300px')
            .addTo(this.mapBox)
        } else {
          this.mapboxPopup.remove()
        }
      }
    }
  }

  // 返回地图当前的缩放级别
  public get getZoom() {
    return this.mapBox ? this.mapBox.getZoom() : 0
  }

  /**
   * 地图缩放
   * @param type
   */
  public mapZoom({ type }: { type: string }) {
    if (type === 'zoomIn') {
      this.mapBox.zoomIn()
    } else {
      this.mapBox.zoomOut()
    }
  }

  /**
   * 注册鼠标移动事件, 显示鼠标位置
   */
  public addMouseEvent() {
    this.mapBox.on('mousemove', ($event: any) => {
      this.lng = $event.lngLat.lng.toFixed(6)
      this.lat = $event.lngLat.lat.toFixed(6)
    })
  }

  /**
   * 添加绘画工具
   */
  public addMapBoxDraw() {
    // const modes:any = {
    //   ...MapboxDraw.modes,
    //   draw_rectangle:DrawRectangle
    // }
    const { modes } = MapboxDraw
    // @ts-ignore
    modes.draw_rectangle = DrawRectangle
    // @ts-ignore
    this.mapBoxDraw = new MapboxDraw({
      displayControlsDefault: false,
      controls: {
        polygon: true,
        trash: true,
      },
    })
    this.mapBox.addControl(this.mapBoxDraw, 'top-left')

    // this.mapBox.on('draw.update', ()=>{
    //   console.log('update')
    // })
    // this.mapBox.on('draw.create', ()=>{
    //   console.log('create')
    // })
  }

  // 所有图层
  public get layerList() {
    return GisStore.layerList
  }

  // 可见图层（带有排列顺序）
  public get layerIndexOrderOfVisible() {
    return GisStore.layerList.filter((item: any) => item.visible === 1)
  }

  /**
   * 需要更新的图层
   */
  public get updateLayerDataAfterGenerateNewField() {
    return GisStore.updateLayerDataAfterGenerateNewField
  }

  /**
   * 需要更新的图层 GeoJson
   */
  @Watch('updateLayerDataAfterGenerateNewField')
  public async updateLayer(layer: any) {
    if (layer) {
      GisStore.setMapLoading(true)
      const response: any = await this.queryLayerDataBySliceGeoJson(layer)
      GisStore.setMapLoading(false)
      const { code } = response.data
      if (code === 100) {
        this.updateLayerAndSourceByLayerId(layer)
      }
    }
  }

  /**
   * 通过图层 Id 移除图层数据
   */
  public updateLayerAndSourceByLayerId(layer: any) {
    this.mapBox.removeLayer(`${layer.id}`)
    this.mapBox.removeSource(`source-${layer.id}`)
    this.checkLayerDataBeforeAddLayer(layer)
  }

  /**
   * 检查已有 图层 是否是显示状态 添加图层
   */
  public async initAddLayerOfVisible() {
    this.initAddEmpty() // 空图层占位
    // 所有图层渲染
    if (this.layerList.length > 0) {
      await this.renderLayerAfterGetLayerData(this.layerList)
      mapUtils.updateMapLayerOrder(this.mapBox, this.layerList) // 调整图层层级
    } else {
      GisStore.setMapLoading(false)
    }
  }

  /**
   * 渲染指定多个图层（先获取数据）
   * @param layers
   */
  public async renderLayerAfterGetLayerData(layers: any[]) {
    if (layers.length > 0) {
      GisStore.setMapLoading(true)
      await this.sliceQueryLayersData(layers) // 分片获取数据
    }
  }

  // 分片获取图层数据  start =================================

  // 获取图层的索引
  public queryLayerDataIndex: number = 0
  // 需要分片加载的图层
  public queryLayers: any[] = []
  /**
   * 分片获取所有地图图层数据 geojson
   */
  public sliceQueryLayersData(layers: any[]) {
    this.queryLayers = layers
    this.queryLayerDataIndex = 0
    this.currentQueryGeojsonLayer = this.queryLayers[this.queryLayerDataIndex]
    this.sliceQueryLayerData()
  }

  /**
   * 获取下一个图层分片数据
   */
  public nextLayerSliceGeojsonData() {
    this.queryLayerDataIndex += 1
    if (this.queryLayers[this.queryLayerDataIndex]) {
      this.currentQueryGeojsonLayer = this.queryLayers[this.queryLayerDataIndex]
      this.sliceQueryLayerData()
      // 没有下一个
    } else {
      mapUtils.gisMapResetByVisibleLayers(
        GisStore.mapboxInstance,
        this.queryLayers,
        GisStore.layerData
      )
      GisStore.setMapLoading(false)
      this.$message.success('图层数据加载完成！')
    }
  }

  public currentQueryGeojsonLayer: any = null // 当前分片获取 geojson 数据的图层
  /**
   * 分片获取单个地图图层数据 geojson 第一片数据起步
   */
  public async sliceQueryLayerData() {
    const { currentQueryGeojsonLayer } = this
    if (currentQueryGeojsonLayer.visible === 1) {
      const data: any = await this.queryLayerDataBySliceGeoJson(
        currentQueryGeojsonLayer
      )
      if (data) {
        GisStore.addLayerDataBySliceGeoJson({
          layerId: currentQueryGeojsonLayer.id,
          geoData: data,
        })
        this.renderLayerByLayerIdAndData(this.currentQueryGeojsonLayer) // 渲染图层
      } else {
        this.$message.warning('图层加载失败！')
      }
      this.nextLayerSliceGeojsonData() // 下一个图层
    } else {
      this.renderLayerByLayerIdAndData(currentQueryGeojsonLayer)
      this.nextLayerSliceGeojsonData() // 下一个图层
    }
  }

  /**
   * 分片获取图层数据接口
   */
  public async queryLayerDataBySliceGeoJson(layer: any) {
    const data: any = await GisStore.queryLayerDataBySliceGeoJson({
      layer,
      projectId: this.projectId,
    })
    return data
  }

  /**
   * 串行渲染图层
   */
  public renderLayerByLayerIdAndData(layer: any) {
    // 添加数据
    this.addOrUpdateSourceToMap(layer.id)
    const beforeId: string = this.getLayerBeforeId(layer.id)
    // 检查是否有 label 图层
    const labelAttributeTag: any = layer.data.tagInfo.attributeTag.find(
      (item: any) => item.tagType === 'label'
    )
    if (labelAttributeTag) {
      mapUtils.addSymbolLabelLayerByConfig(
        this.mapBox,
        layer,
        labelAttributeTag.config,
        beforeId
      )
    }
    this.addLayerToMap(layer, beforeId)
  }

  // 分片获取图层数据  end =================================

  /**
   * 获取多个图层的渲染数据 （不分片获取）
   */
  public async getLayerDataByLayers(layers: any[]) {
    const requests = layers
      .filter((layer: any) => layer.visible === 1)
      .map(async (layer: any) => {
        return this.queryLayerDataByLayerId(layer.id)
      })
    await Promise.allSettled(requests)
  }

  /**
   * 渲染所有有数据的图层
   * @private
   */
  private renderVisibleLayer(layers: any[]) {
    layers.forEach((layer: any) => {
      const beforeId: string = this.getLayerBeforeId(layer.id)
      // 检查是否有 label 图层
      this.addOrUpdateSourceToMap(layer.id)
      const labelAttributeTag: any = layer.data.tagInfo.attributeTag.find(
        (item: any) => item.tagType === 'label'
      )
      if (labelAttributeTag) {
        mapUtils.addSymbolLabelLayerByConfig(
          this.mapBox,
          layer,
          labelAttributeTag.config,
          beforeId
        )
      }
      this.addLayerToMap(layer, beforeId)
    })
  }

  /**
   * 图层拖拽
   * @param $event
   */
  public async dragAddLayerToMap($event: any) {
    if (!$event.dataTransfer.types.includes('layer')) {
      $event.preventDefault()
      return
    }
    const newNodeData = $event.dataTransfer.getData('layer').split(':')
    const layerId: number = Number(newNodeData[1])
    const dragLayer: any = this.layerList.find(
      (layer: any) => layer.id === layerId
    )
    const mapboxLayerId: string = layerId.toString()
    const checkLayerExist: boolean = this.mapBox.getLayer(mapboxLayerId)
    // 当前图层已存在
    if (checkLayerExist) {
      this.checkLayerView(layerId)
    } else {
      await this.checkLayerDataBeforeAddLayer(dragLayer)
      await this.afterAddLayer(layerId, 1)
    }
  }

  /**
   * 获取 定位图层位置 beforeId， 用于添加图层是定位
   * @private
   */
  private getLayerBeforeId(layerId: number) {
    // const visibleLayers: any[] = this.layerList.filter(
    //   (layer: any) => layer.visible === 1
    // )
    const visibleLayers: any[] = this.layerList
    const index: number = visibleLayers.findIndex(
      (layer: any) => layer.id === layerId
    )
    let beforeId: string | null = 'base-empty-layer'
    if (index > 0 && visibleLayers[index - 1].id) {
      const layerIdString: string = String(visibleLayers[index - 1].id)
      const beforeIdExist: boolean = !!this.mapBox.getLayer(layerIdString)
      beforeId = beforeIdExist ? layerIdString : 'base-empty-layer'
    }
    return beforeId
  }

  /**
   * 添加图层之前的检查图层数据
   */
  public async checkLayerDataBeforeAddLayer(layer: any) {
    const { id } = layer
    // 定位图层位置
    const beforeId: string = this.getLayerBeforeId(id)
    if (GisStore.layerData[id]) {
      this.addOrUpdateSourceToMap(id)
      this.addLayerToMap(layer, beforeId)
      return
    }
    // 获取图层数据
    GisStore.setMapLoading(true)
    const data: any = await this.queryLayerDataBySliceGeoJson(layer)
    if (data) {
      GisStore.addLayerDataBySliceGeoJson({
        layerId: layer.id,
        geoData: data,
      })
      this.addOrUpdateSourceToMap(layer.id)
      this.addLayerToMap(layer, beforeId)
    } else {
      this.$message.error('图层加载失败！')
    }
    GisStore.setMapLoading(false)
  }

  /**
   * 添加 / 更新 数据源
   * @param id
   */
  public addOrUpdateSourceToMap(id: number) {
    const layerDataInfo: any = GisStore.layerData[id] || { geojson: null }
    const sourceId: string = `source-${id}`
    const source: any = {
      type: 'geojson',
      data: layerDataInfo.geojson,
    }
    if (!this.mapBox.getSource(sourceId)) {
      this.mapBox.addSource(sourceId, source)
    } else {
      this.mapBox.getSource(sourceId).setData(source.data)
    }
  }

  /**
   * 添加图层
   * @param layer
   * @param beforeId
   */
  public addLayerToMap(layer: any, beforeId: string) {
    const { id, type } = layer
    const layerId: number = Number(id)
    if (type === 'circle') {
      this.addPoint(layer, beforeId)
    } else if (type === 'line') {
      this.addLine(layer, beforeId)
    } else if (type === 'fill') {
      this.addPolygon(layer, beforeId)
    }
    this.addMouseEventToMapBoxByLayerId(String(layerId))
  }

  /**
   * 图层重复拖拽
   * @param layerId
   */
  public checkLayerView(layerId: number) {
    const layerViewState: any = this.mapBox.getLayoutProperty(
      String(layerId),
      'visibility'
    )
    if (layerViewState === 'none') {
      this.$confirm({
        title: '当前图层已存在, 是否设置该图层可见？',
        okText: '确定',
        cancelText: '取消',
        centered: true,
        onOk: () => {
          this.mapBox.setLayoutProperty(
            String(layerId),
            'visibility',
            'visible'
          )
          this.afterAddLayer(layerId, 1)
        },
      })
    } else {
      this.$message.warn('该图层已存在！')
    }
  }

  /**
   * 添加图层 后
   */
  public async afterAddLayer(layerId: number, value: number) {
    await this.editLayerSeen(layerId, value)
  }

  /**
   * 图层管理 图层更新状态
   * show - 显示隐藏， delete - 删除（单个/多个）fit-bounds - 复位功能， load-layer - 加载数据(支持多个图层)
   * @param info
   */
  public layerUpdate(info: any) {
    const { type, value } = info
    switch (type) {
      case 'show':
        this.setMapBoxLayoutProperty(value)
        break
      case 'load-layer':
        this.renderLayerAfterGetLayerData(value)
        break
      case 'order':
        mapUtils.updateMapLayerOrder(this.mapBox, this.layerList)
        break
      default:
        break
    }
  }

  /**
   * 当前 激活的工具栏项
   */
  public get currentToolBarAction() {
    return GisStore.currentToolBarAction
  }

  /**
   * toolBar 行为动作
   */
  @Watch('currentToolBarAction')
  public watchCurrentToolBarAction(action: any) {
    switch (action) {
      case 'gisPanVisible': // 平移
        this.gisMapPan()
        break
      case 'gisBoxSelectVisible': // 框选
        this.gisSelectBox()
        break
      default:
        this.mapBox.dragPan.enable()
        break
    }
  }

  /**
   * 单个图层复位， 复位功能 truf.js
   * @param layer
   */
  public async mapBoxFitBoundsByLayer(layer: any) {
    let currentLayerData: LayerData | null = GisStore.layerData[layer.id]
    if (!currentLayerData) {
      const data: any = await this.queryLayerDataBySliceGeoJson(layer)
      if (data) {
        currentLayerData = data
      }
    }
    if (currentLayerData) {
      const bound: any[] = turf.bbox(currentLayerData.geojson)
      this.mapBoxFitBounds(bound)
    }
  }

  /**
   * 根据两角坐标设定边界适应窗口
   * @param bounds
   */
  public mapBoxFitBounds(bounds: any[]) {
    this.mapBox.fitBounds(bounds, { padding: 50 })
  }

  /**
   * 获取图层数据 by layerId (不分片获取)
   */
  public async queryLayerDataByLayerId(layerId: number) {
    const config = {
      parameters: {
        projectId: this.projectId,
        layerId,
      },
      tabId: this.currentTabId,
    }
    const response: any = await GisStore.queryLayerDataById(config)
    return response
  }

  /**
   * 平移
   */
  public gisMapPan() {
    this.mapBox.dragPan.enable()
    GisStore.setCurrentToolBarAction(null)
  }

  /**
   * 框选
   */
  public selectBoxPoints: any = []
  public gisSelectBox() {
    this.mapBoxDraw.changeMode('draw_rectangle')
    this.mapBox.dragRotate.disable()

    this.mapBox.on('draw.update', (features: any) => {
      this.selectBoxPoints = features
    })
    this.mapBox.on('draw.create', (features: any) => {
      this.selectBoxPoints = features
    })
  }

  /**
   * 修改图层隐藏显示
   */
  public async setMapBoxLayoutProperty(layer: any) {
    const { id, visible } = layer
    const layerId: string = id.toString()
    const currentLayerDataExist = !!GisStore.layerData[layer.id]
    // 图层数据存在
    if (currentLayerDataExist) {
      // 如果该图层已存在， 直接修改显示状态， 否则走获取数据加载图层那一套逻辑
      const checkLayerExist: boolean = GisStore.mapboxInstance.getLayer(layerId)
      if (checkLayerExist) {
        // TODO 后续统一到保存
        this.updateLayerVisible(layerId, visible)
        await this.editLayerSeen(id, visible)
      }
      // 数据不存在
    } else {
      GisStore.setMapLoading(true)
      const data: any = await this.queryLayerDataBySliceGeoJson(layer)
      if (data) {
        GisStore.addLayerDataBySliceGeoJson({
          layerId: id,
          geoData: data,
        })
        this.addOrUpdateSourceToMap(id) // 更新数据
        this.updateLayerVisible(layerId, visible)
        // 线图层，有箭头
        if (layer.type === 'line' && layer.data?.hasArrow) {
          this.lineLayerRenderArrow(layer)
        }
        await this.editLayerSeen(id, visible)
      } else {
        this.$message.warning('图层加载失败！')
      }
      GisStore.setMapLoading(false)
    }
  }

  /**
   * 渲染线图层的箭头
   * @param layer
   */
  public lineLayerRenderArrow(layer: any) {
    const layerDataInfo: any = GisStore.layerData[layer.id] || {
      geojson: null,
    }
    const layerId: string = layer.id.toString()
    const { geojson } = layerDataInfo
    mapUtils.updateArrowLayerWithData(GisStore.mapboxInstance, layer, geojson)
    const layerArrowId: string = `arrow-layer-${layerId}`
    this.updateLayerVisible(layerArrowId, 1)
  }

  /**
   * 修改图层显示隐藏状态
   * @param layerId
   * @param visible
   * @private
   */
  private updateLayerVisible(layerId: string, visible: number) {
    const value: string = visible === 1 ? 'visible' : 'none'
    GisStore.mapboxInstance.setLayoutProperty(layerId, 'visibility', value)
    const layerLabelId: string = `label-${layerId}`
    const checkLayerLabelExist: boolean =
      !!GisStore.mapboxInstance.getLayer(layerLabelId)
    if (checkLayerLabelExist) {
      GisStore.mapboxInstance.setLayoutProperty(
        layerLabelId,
        'visibility',
        value
      )
    }
    const layerArrowId: string = `arrow-layer-${layerId}`
    const checkLayerArrowExist: boolean =
      !!GisStore.mapboxInstance.getLayer(layerArrowId)
    if (checkLayerArrowExist) {
      GisStore.mapboxInstance.setLayoutProperty(
        layerArrowId,
        'visibility',
        value
      )
    }
  }

  /**
   * 请求接口修改图层显示状态
   */
  public editLayerSeen(layerId: number, visible: number) {
    const parameters: any = {
      projectId: this.projectId,
      layerId,
      visible,
    }
    GisStore.editLayerSeen(parameters)
  }

  /**
   * 删除图层 通过图层管理删除
   * @param layersId
   */
  public deleteLayers(layersId: any[]) {
    layersId.forEach((id: any) => {
      if (this.mapBox.getLayer(String(id))) {
        this.mapBox.removeLayer(String(id))
      }
      if (this.mapBox.getSource(`source-${id}`)) {
        this.mapBox.removeSource(`source-${id}`)
      }
    })
  }
}
</script>

<style scoped lang="less">
@import '@/components/gis/styles/scale-control.less';

.gis-map-component {
  height: 100%;
  position: relative;

  .map-class {
    height: 100%;
    position: relative;
    width: 100%;
  }

  .layer-loading {
    align-items: center;
    background-color: rgba(87, 96, 230, 0.1);
    display: flex;
    height: 100%;
    justify-content: center;
    left: 0;
    position: absolute;
    top: 0;
    width: 100%;
    z-index: 100;
  }
}
</style>
